package ofdinv

import (
	"encoding/xml"
	"fmt"
	"sort"
)

// Page 表示OFD页面结构（根元素为Page）
type Page struct {
	XMLName  xml.Name      `xml:"http://www.ofdspec.org/2016 Page"` // XML根元素，标识页面，包含命名空间
	Area     PageArea      `xml:"Area"`                             // 页面区域定义，包含物理尺寸信息
	Template *PageTemplate `xml:"Template,omitempty"`               // 模板引用，指向页面使用的模板（可选）
	Content  PageContent   `xml:"Content"`                          // 页面内容容器，包含所有图层和对象
}

// PageArea 表示页面区域
type PageArea struct {
	XMLName     xml.Name `xml:"Area"`        // XML元素名称，标识页面区域
	PhysicalBox string   `xml:"PhysicalBox"` // 物理区域框坐标，定义页面的实际尺寸和位置
}

// PageTemplate 表示页面中对模板的引用
type PageTemplate struct {
	XMLName    xml.Name `xml:"Template"`        // XML元素名称，标识模板引用
	TemplateID int      `xml:"TemplateID,attr"` // 模板ID，引用具体模板的标识符
	ZOrder     string   `xml:"ZOrder,attr"`     // 渲染顺序，控制模板在页面中的叠加顺序
}

// PageContent 表示页面内容
type PageContent struct {
	XMLName xml.Name    `xml:"Content"` // XML元素名称，标识页面内容容器
	Layers  []PageLayer `xml:"Layer"`   // 图层集合，每个图层包含不同类型的图形对象
}

// PageLayer 表示页面图层
type PageLayer struct {
	XMLName      xml.Name          `xml:"Layer"`       // XML元素名称，标识图层
	ID           int               `xml:"ID,attr"`     // 图层唯一标识符，用于区分不同图层
	TextObjects  []TextObject      `xml:"TextObject"`  // 文本对象集合，包含页面中的文本元素
	ImageObjects []PageImageObject `xml:"ImageObject"` // 图像对象集合，包含页面中的图像元素
	PathObjects  []PagePathObject  `xml:"PathObject"`  // 路径对象集合，包含页面中的图形路径
}

// PageImageObject 表示页面图像对象
type PageImageObject struct {
	XMLName    xml.Name `xml:"ImageObject"`     // XML元素名称，标识图像对象
	ID         int      `xml:"ID,attr"`         // 图像对象唯一标识符
	CTM        string   `xml:"CTM,attr"`        // 当前变换矩阵，定义图像的缩放、旋转等变换
	Boundary   string   `xml:"Boundary,attr"`   // 图像对象边界框坐标
	ResourceID int64    `xml:"ResourceID,attr"` // 图像资源引用ID，指向文档中的图像资源
}

// PagePathObject 表示页面路径对象
type PagePathObject struct {
	XMLName         xml.Name `xml:"PathObject"`          // XML元素名称，标识路径对象
	ID              int      `xml:"ID,attr"`             // 路径对象唯一标识符
	Boundary        string   `xml:"Boundary,attr"`       // 路径对象边界框坐标
	LineWidth       float64  `xml:"LineWidth,attr"`      // 线宽，定义路径描边的宽度
	Fill            string   `xml:"Fill,attr,omitempty"` // 填充样式，定义路径的填充方式（可选）
	AbbreviatedData string   `xml:"AbbreviatedData"`     // 简化的路径数据，描述路径的形状和绘制命令
}

// TextObject 表示通用文本对象
type TextObject struct {
	XMLName     xml.Name    `xml:"TextObject"`         // XML元素名称，标识文本对象
	ID          int         `xml:"ID,attr"`            // 文本对象唯一标识符，用于引用和查找
	Boundary    Boundary    `xml:"Boundary,attr"`      // 文本对象边界框坐标，格式为"x y width height"
	Font        string      `xml:"Font,attr"`          // 字体资源引用ID，指向文档中的字体资源
	Size        float64     `xml:"Size,attr"`          // 字体大小，单位为磅(pt)
	CTM         string      `xml:"CTM,attr,omitempty"` // 当前变换矩阵，定义文本的缩放、旋转等变换（可选）
	CGTransform CGTransform `xml:"CGTransform"`        // 字符变换信息，控制字符的显示效果
	TextCode    TextCode    `xml:"TextCode"`           // 文本代码，包含实际的文本内容和位置信息
}

// CGTransform 表示通用字符变换
type CGTransform struct {
	XMLName      xml.Name `xml:"CGTransform"`       // XML元素名称，标识字符变换
	CodePosition int      `xml:"CodePosition,attr"` // 代码位置，标识变换应用的起始字符位置
	CodeCount    int      `xml:"CodeCount,attr"`    // 代码数量，指定应用变换的字符个数
	GlyphCount   int      `xml:"GlyphCount,attr"`   // 字形数量，变换后产生的字形数量
	Glyphs       string   `xml:"Glyphs"`            // 字形序列，变换后的实际字形代码
}

// TextCode 表示通用文本代码
type TextCode struct {
	XMLName xml.Name `xml:"TextCode"`              // XML元素名称，标识文本代码
	X       float64  `xml:"X,attr"`                // 文本起始点的X坐标
	Y       float64  `xml:"Y,attr"`                // 文本起始点的Y坐标
	DeltaX  DeltaX   `xml:"DeltaX,attr,omitempty"` // 字符间距调整序列，控制字符间的水平间距（可选）
	Text    string   `xml:",chardata"`             // 实际文本内容，包含显示的字符串
}

func (p *Page) GetTemplateID() int {
	return p.Template.TemplateID
}
func (p *Page) GetAllTextObject() []TextObject {
	var objs []TextObject
	for _, layer := range p.Content.Layers {
		objs = append(objs, layer.TextObjects...)
	}
	return objs
}

// 每次执行本方法，
// 都会先执行一遍(p *Page) GetAllTextObject() []TextObject，
// 如果代码中需要频繁通过ID获取text，
// 可以直接运行GetAllTextObject() 方法，
// 然后就可以反复使用切片返回值了
func (p *Page) GetTextByID(ID int) (string, error) {
	for _, obj := range p.GetAllTextObject() {
		if obj.ID == ID {
			return obj.TextCode.Text, nil
		}
	}
	return "", fmt.Errorf("页面中未找到ID为 %d 的映射", ID)
}

// 获取页面的[]TextObject中ID最小的object的文本
func (p *Page) GetMinimumIDTextObject() (string, error) {
	objs := p.GetAllTextObject()
	if len(objs) == 0 {
		return "", fmt.Errorf("页面中没有TextObject")
	}
	sort.Slice(objs, func(i, j int) bool {
		return objs[i].ID < objs[j].ID
	})
	return objs[0].TextCode.Text, nil
}
